#ifndef PDBMANAGER_H
#define PDBMANAGER_H

extern "C" {
	#include "Matt/MultipleAlignment.h"
	#include "Matt/MultipleAlignmentOutput.h"
}

#define REDRAW_RENDER			0x0001
#define REDRAW_RESIDUES			0x0002
#define REDRAW_SEQUENCE			0x0004
#define REDRAW_UPDATE_CHECKS	0x0100
#define REDRAW_UPDATE_SELECTION	0x0200
#define REDRAW_WINDOWS_ONLY		0x00FF
#define REDRAW_ALL				0xFFFF

// Calls Update(), too.  Basically only used when creating a drag rectangle
// on the 3D display.
#define REDRAW_NOW     0x10000

extern "C" {
	#include "Matt/pdb.h"
}

#include "SharedStatusBar.h"

class MainWindow;

// For NormalVertex
#include "glUtil.h"

struct Transform {
	Vector translation;

	// Only used for dragging with arc ball rotation.
	Matrix currentRot;

	// All other rotations.
	Matrix worldView;

	void CalcFullCurrentRotation(Matrix *out, PDBManager *pdbManager);
	void KeepCurrentRotation(PDBManager *pdbManager);
};

struct ManagedChain;

struct HBondInfo {
	ManagedChain *chain1;
	ManagedChain *chain2;

	int res1;
	int atom1;

	int res2;
	int atom2;
};

struct ManagedModel : public PDBModel {
	Vector min;
	Vector max;

	HBondInfo *hBonds;
	int numHbonds;
};

struct ConnectionInfo {
	PDBAtom *destAtom;
};

#define ATOM_BACKBONE	0x0001

struct ExtendedAtomInfo {
	int firstConnectionIndex;
	unsigned short atomicIntSymbol;
	//unsigned short flags;
};


#define RESIDUE_HAS_SPLINE_BEFORE		0x00000001
#define RESIDUE_HAS_SPLINE_AFTER		0x00000002


#define RESIDUE_IS_ALPHA				0x00000004
#define RESIDUE_IS_BETA					0x00000008

#define RESIDUE_SECONDARY				0x0000000C


#define RESIDUE_IS_RESIDUE				0x00000100
#define RESIDUE_IS_DNA					0x00000200

#define RESIDUE_TYPE					0x00000F00


#define RESIDUE_RENDER_CARTOON			0x00010000
#define RESIDUE_RENDER_BALL_AND_STICK	0x00020000
#define RESIDUE_RENDER_SPACEFILL		0x00040000

#define RESIDUE_RENDER_LABEL			0x00800000
#define RESIDUE_RENDER_HBONDS			0x00400000

// Everything but label.  Note that can show labels
// for hidden residues, at the moment.
#define RESIDUE_RENDER_3D				0x003F0000
#define RESIDUE_RENDER					0x00FF0000

// If not set, use custom color.
#define RESIDUE_COLOR_STRUCTURE			0x01000000
#define RESIDUE_COLOR_ALIGNMENT			0x02000000
#define RESIDUE_FORCED_COLOR			0x0F000000

// For multiple alignments only.
#define RESIDUE_ALIGNED					0x10000000

// Separate toggle only affecting draw modes that
// draw/color individual atoms.
#define RESIDUE_COLOR_ATOMS				0x20000000

#define RESIDUE_SELECTED				0x80000000
#define RESIDUE_LAST_SELECTED			0x40000000

enum UndoType {
	UNDO_TRANSFORMATION,
	UNDO_FLAGS,
};

struct ResUndoInfo {
	unsigned int flags;
	Color color;
};

struct UndoBuffer {
	UndoBuffer *prev;
	ResUndoInfo info[1];
	/*UndoType type;
	union {
		ResUndoInfo *
	};//*/
};


/*struct UndoBufferEntry {
	ResidueIndex index;
	int numResidues;
	unsigned int setFlags;
	unsigned int clearFlags;
};//*/

/*struct UndoFlagsBuffer : public UndoBuffer {
	int numEntries;
	UndoBufferEntry entries[1];
};

struct MiniUndoInfo {
	int chain;
	int firstRes;
	int lastRes;
	union {
		Color color;
		unsigned int xorFlags;
	};
};//*/

struct ExtendedResidueInfo {
	unsigned int flags;
	Color color;
};

struct ManagedChain : public PDBChain {
	ManagedModel *model;

	ConnectionInfo *connectionInfo;
	ExtendedAtomInfo * atomInfo;
	ExtendedResidueInfo *residueInfo;

	// Redundant value - just duplicates length.
	int numChainResidues;

	int firstAtomIndex;
	// actually last residue index +1.
	int lastAtomIndex;
};

struct ResidueIndex {
	int model;
	int chain;
	int residue;
};

// For multiple alignments, can be shared between multiple top level windows.
class PDBManager {
private:
	MainWindow **windows;
	int numWindows;

	void ClearUndoBuffer(UndoBuffer *&buffer);
	void SingleUndo(UndoBuffer *&undo, UndoBuffer *&redo);
	UndoBuffer *MakeUndoState();

public:
	Transform transform;

	SequenceIndexAlignment *align;

	void ClearPDB();
	int LoadPDB(PDBData *pdb);
	void CalcBounds(Vector *min, Vector *max, int selected, int model);

	void AddWindow(MainWindow *window);
	// Auto deletes when no associated windows.
	int RemoveWindow(MainWindow *window);

	Vector selCenter;

	void CalcSelectionCenter();
	int IsChainSelected(int chain);
	void SelectChain(int chain);
	void UnselectChain(int chain);
	void ToggleChainSelection(int chain);

	UndoBuffer *undo;
	UndoBuffer *redo;

	ManagedModel **models;
	int numModels;
	ManagedChain **chains;
	int numChains;

	// Scales models so that they fit on screen.
	float scale;

	PDBManager(MainWindow *window);
	~PDBManager();

	void CalcMatrices();

	void CalcConnections(ManagedModel *model);
	void CalcConnections();

	void GetMinMaxFlags(unsigned int reqFlags, unsigned int *min, unsigned int *max, int forceVisible=0);
	void ToggleSelectedFlags(unsigned int newFlags, unsigned int clearFlagsIfSet, unsigned int clearFlagsIfUnset, int redraw=REDRAW_ALL);
	void UpdateSelectedResiduesColor(Color color, int redraw=REDRAW_ALL);
	void UpdateSelectedResidues(unsigned int newFlags, unsigned int clearFlags, int redraw=REDRAW_ALL);
	void UpdateResidues(unsigned int reqFlags, unsigned int newFlags, unsigned int clearFlags, int redraw);
	int UpdateResidues2(unsigned int reqFlagMask, unsigned int reqFlags, unsigned int newFlags, unsigned int clearFlags, int redraw);

	void ClearUndo();
	void ClearRedo();

	void SaveUndoState();
	void Undo();
	void Redo();

	int GetChainModelIndex(int chain);

	void Redraw(unsigned int flags = REDRAW_ALL, int chain = -1, int residue = -1);
	void ChangeSelection(int chain, int residue);
	inline int IsShared() {
		return numWindows > 1;
	}
	int LoadAlignment(MultipleAlignment *ma);

	int TryAddChain(ManagedModel *model, int index);

	void InvertSelection();

	Color GetResidueColor(int chain, int index, int ignoreSelection=0);
};

/*

class TopLevelWindow {
	virtual void ContextMenu() = 0;


	virtual void Redraw(unsigned int flags = REDRAW_ALL, int chain = -1, int residue = -1) = 0;
	virtual void UpdateChecks() = 0;
};
//*/
#endif
